#include "dht11.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
#include "log.h"

static char *TAG = "dht11"; /*.c TAG*/

/*
    DHT11的时序:
    1. 主机发送起始信号（低电平至少18ms，高电平20-40μs）
    2. DHT11响应主机，发送响应信号（低电平80μs，高电平80μs）
    3. DHT11发送数据，共40位
    4. 每一位数据的时序为：低电平50μs，高电平26-28μs
    5. 数据的前16位为湿度数据，后16位为温度数据
    6. 主机读取数据后拉高总线，至少等待1s后再次读取数据
*/

/**
 * @brief dht11_gpio_mode
 * @note  -- dht11引脚模式
 * @param mode 1-OUTPUT  other-INPUT
 * @param dht_data 结构体指针
 */
static void dht11_gpio_mode(uint8_t mode, dht_t *dht_data)
{
    if (mode)
    {
        GPIO_InitTypeDef GPIO_InitStruct;
        GPIO_InitStruct.Pin = dht_data->dht_pin;
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(dht_data->dht_port, &GPIO_InitStruct);
    }
    else
    {
        GPIO_InitTypeDef GPIO_InitStruct;
        GPIO_InitStruct.Pin = dht_data->dht_pin;
        GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        HAL_GPIO_Init(dht_data->dht_port, &GPIO_InitStruct);
    }
}

/**
 * @brief DHT11_Start
 * @note  -- DHT11起始信号
 * @param dht_data 结构体指针
 */
static void DHT11_Start(dht_t *dht_data)
{
    dht11_gpio_mode(1, dht_data);                                             // set gpio mode outpull
    HAL_GPIO_WritePin(dht_data->dht_port, dht_data->dht_pin, GPIO_PIN_RESET); // LOW >=18ms
    delay_ms_tim(20);                                                         //>=18ms
    HAL_GPIO_WritePin(dht_data->dht_port, dht_data->dht_pin, GPIO_PIN_SET);   // HIGH wait dht11
    user_delaynus_tim(30);                                                    // 30us
}

/**
 * @brief  DHT11_Read_Byte -- 读取DHT11一个字节(8bit)
 * @note   -- 读取DHT11一个字节(8bit)
 * @param dht_data 结构体指针
 * @return uint8_t
 */
static uint8_t DHT11_Read_Byte(dht_t *dht_data)
{
    uint8_t byte = 0;

    for (uint8_t i = 0; i < 8; i++)
    {
        while (!HAL_GPIO_ReadPin(dht_data->dht_port, dht_data->dht_pin))
            ;                                                             // 等待50us低电平过去
        user_delaynus_tim(60);                                            // 延时60us，如果还为高则数据为1，否则为0
        byte <<= 1;                                                       // 左移位使正确接收8位数据，数据为0时直接移位
        if (HAL_GPIO_ReadPin(dht_data->dht_port, dht_data->dht_pin) == 1) // 数据为1时，使dat加1来接收数据1
        {
            byte += 1;
        }
        while (HAL_GPIO_ReadPin(dht_data->dht_port, dht_data->dht_pin))
            ;
    }
    return byte;
}

/**
 * @brief DHT11_Read_Temperature_Humidity
 * @note  --获取DHT11温湿度数据
 * @param dht_data 结构体指针
 */
void DHT11_Read_Temperature_Humidity(dht_t *dht_data)
{
    uint8_t data[5];
    DHT11_Start(dht_data);
    dht11_gpio_mode(0, dht_data); // set gpio mode inpull
    if (HAL_GPIO_ReadPin(dht_data->dht_port, dht_data->dht_pin) == 0)
    {
        while (HAL_GPIO_ReadPin(dht_data->dht_port, dht_data->dht_pin) == 0)
            ;
        while (HAL_GPIO_ReadPin(dht_data->dht_port, dht_data->dht_pin) == 1)
            ;
        data[0] = DHT11_Read_Byte(dht_data); // 接收湿度高八位
        data[1] = DHT11_Read_Byte(dht_data); // 接收湿度低八位
        data[2] = DHT11_Read_Byte(dht_data); // 接收温度高八位
        data[3] = DHT11_Read_Byte(dht_data); // 接收温度低八位
        data[4] = DHT11_Read_Byte(dht_data); // 接收校正位
        if ((data[0] + data[1] + data[2] + data[3]) == data[4])
        {
            dht_data->humi = data[0] * 1.0 + data[1] / 10.0;
            dht_data->temp = data[2] * 1.0 + data[3] / 10.0;
        }
        else
        {
            MY_LOGI(TAG, "DHT11 CHECK ERROR");
        }
    }
}
